#!/usr/bin/python
import simplejson, MuConnection, time
from StringIO import StringIO

""" MuTestApi contains the basic REST commands necisary to execute all Studio Verify functions 
	
	Classes contained: 
	MuTestApi - This class is instantiated to run Studio Verify commands and recieves one argument:
		MuConnection - The connection class for maintaining a SSL session to the Mu Test Suite

	TestCase: Not implemented yet: This class will contain a single line of data to execute a test case with Studio Verify
	
	Option: this class is a container for the Options used in verrifying the scenario and in a TestCase
	
	Result: this class is used to reperesent the result of exeacuting a test case.
	
"""

class MuTestApi():
	""" MuTestApi: This class is instantiated to run Studio Verify commands and recieves one argument:

		Arguments: 
		MuConnection - The connection class for maintaining a SSL session to the Mu Test Suite

		Globals:
		muConnection - The SSL connection manager
		session - containes the active session id for this instance
		VERBOSE - enabel detaield printing to screen.
	
		TODO:
		logging - Used to loog activiteis instead of VERBOSE
		TestCase - a class that represents a single test case instance
	
	"""
	muConnection = None
	session = None
	VERBOSE = False
	
	def __init__(self,connection):
		""" set up the TestAPI with the passed in MuConnection """
		self.muConnection = connection
		
	def newSession(self):
		""" initiate a new TestApi session for testing 
			Example URL: https://$MU_IP/api/v5/ddt/newSession
			
			Arguments:
			None
			
			Returns:
			MuConnection.MuResponseObject
		"""
		url = self.muConnection.TOP_LEVEL_URL + "/api/v5/ddt/newSession"
		responseData = self.muConnection.post(url, None, None,True)
		self.session = responseData
		if self.VERBOSE:
			print responseData
		return responseData
		
	def getAllSessions(self):
		""" Used to get a list of all active sessions on the Mu Test Suite. No active Session is required
			Example URL: https://$MU_IP/api/v5/ddt/getAllSessions
			
			Arguments:
			None
			
			Returns:
			MuConnection.MuResponseObject
		"""
		url = self.muConnection.TOP_LEVEL_URL + "/api/v5/ddt/getAllSessions"
		responseData = self.muConnection.get(url, True)
		self.session = responseData
		if self.VERBOSE:
			print responseData
		return responseData
		
	def closeAllSessions(self):
		""" Used to close all active sessions on the Mu Test Suite. No active Session is required
			Example URL: https://$MU_IP/api/v5/ddt/closeAllSessions
			
			Arguments: 
			None
			
			Returns:
			MuConnection.MuResponseObject
		"""
		url = self.muConnection.TOP_LEVEL_URL + "/api/v5/ddt/closeAllSessions"
		responseData = self.muConnection.post(url, None, None, True)
		self.session = responseData
		if self.VERBOSE:
			print responseData
		return responseData
		
	def closeSession(self):
		""" Used to close the active sessions on the Mu Test Suite. An active Session is required
			Example URL: https://$MU_IP/api/v5/ddt/session/closeSession
			
			Arguments:
			None
			
			Returns:
			MuConnection.MuResponseObject
		"""
		url = self.muConnection.TOP_LEVEL_URL + "/api/v5/ddt/session/closeSession"
		responseData = self.muConnection.get(url,True)
		self.session = responseData
		if self.VERBOSE:
			print responseData
		return responseData
		
		
	def loadScenario(self,uuid):
		""" Used to load a scenario into the active test session on the Mu. The scenario must 
			reside on the Mu prior to this commands execution. An active session is required.
			Example URL: https://$MU_IP/api/v5/ddt/session/loadScenario/<scenario_uuid>
			
			Arguments:
			uuid - The uuid of the scenario on the Mu Test Suite
			
			Returns:
			MuConnection.MuResponseObject
		"""
		url = self.muConnection.TOP_LEVEL_URL + "/api/v5/ddt/session/loadScenario/" + uuid
		responseData = self.muConnection.post(url, None, None, True)
		self.session = responseData
		if self.VERBOSE:
			print responseData
		return responseData
		
	def	setHost(self,hostRole,hostName):
		""" Used to set the source and/or traget hosts in a scenario. This must be done prior to 
			scenario verification or a test run. An active session is required.
			Example URL: https://$MU_IP/api/v5/ddt/session/scenario/hosts/<host_role_1>/<host_name_1>
			
			Arguments:
			hostRole - The host tole specified in the scenario
			hostName - The name of the host, a Mu interface or a host from the host table 
			
			Returns:
			MuConnection.MuResponseObject
		"""
		url = self.muConnection.TOP_LEVEL_URL + "/api/v5/ddt/session/scenario/hosts/" + hostRole + "/" + hostName
		responseData = self.muConnection.post(url, None, None, True)
		self.session = responseData
		if self.VERBOSE:
			print responseData
		return responseData
		
	def getHosts(self):
		""" Used to get the host roles in a scenario. An active session is required.
			Example URL: https://$MU_IP/api/v5/ddt/session/scenario/hosts
			
			Arguments:
			None
			
			Returns:
			MuConnection.MuResponseObject
		"""
		url = self.muConnection.TOP_LEVEL_URL + "/api/v5/ddt/session/scenario/hosts"
		responseData = self.muConnection.get(url,True)
		if responseData.getRetrievedObjectType()==responseData.JSON and not responseData.hasError():
			jasonData = responseData.getJsonObject()
			tmpResponse = jasonData["hosts"]
			hostListData = tmpResponse["host"]
			responseData.setRetrievedObject(hostListData)
			responseData.setRetrievedObjectType(responseData.LIST)
		self.session = responseData
		if self.VERBOSE:
			print responseData
		return responseData
		
	def getChannels(self):
		""" Used to get the channel names in a scenario if any exists. An active session is required.
			Example URL: https://$MU_IP/api/v5/ddt/session/scenario/channels
			
			Arguments:
			None
			
			Returns:
			MuConnection.MuResponseObject
		"""
		url = self.muConnection.TOP_LEVEL_URL + "/api/v5/ddt/session/scenario/channels"
		responseData = self.muConnection.get(url, True)
		if responseData.getRetrievedObjectType()==responseData.JSON and not responseData.hasError():
			jasonData = responseData.getJsonObject()
			tmpResponse = jasonData["channels"]
			if tmpResponse!='':
				channelListData = tmpResponse["channel"]
				responseData.setRetrievedObject(channelListData)
				responseData.setRetrievedObjectType(responseData.LIST)
		self.session = responseData
		if self.VERBOSE:
			print responseData
		return responseData
		
	def setChannels(self,channelName,hostName):
		""" Used to set the channel hosts in a scenario. If channes are present this must be done
			prior to a verification or a test suite run. An active session is required.
			Example URL: https://$MU_IP/api/v5/ddt/session/scenario/channels/<channelName>/<hostName>
			
			Arguments:
			channelName - the name of the channel specified in he sc senario. normaly channel, channel_2, etc.
			hostName - the name of the host as it occurs in the Mu Netowrk Host table
			
			Returns:
			MuConnection.MuResponseObject
		"""
		url = self.muConnection.TOP_LEVEL_URL + "/api/v5/ddt/session/scenario/channels/" + channelName + "/" + hostName
		responseData = self.muConnection.post(url, None, None, True)
		self.session = responseData
		if self.VERBOSE:
			print responseData
		return responseData
		
		
	def getOptions(self):
		""" Used to get the scenario options list from the loaded scenario. An active session is required.
			Example URL: https://$MU_IP/api/v5/ddt/session/scenario/options
			
			Arguments:
			None
			
			Returns:
			MuConnection.MuResponseObject
		"""
		url = self.muConnection.TOP_LEVEL_URL + "/api/v5/ddt/session/scenario/options"
		responseData = self.muConnection.get(url, True)
		if responseData.getRetrievedObjectType()==responseData.JSON and not responseData.hasError():
			jasonData = responseData.getJsonObject()
			tmpResponse = jasonData["options"]
			if tmpResponse != '':
				optionListData = tmpResponse["option"]
				responseData.setRetrievedObject(optionListData)
				responseData.setRetrievedObjectType(responseData.LIST)
		self.session = responseData
		if self.VERBOSE:
			print responseData
		return responseData
		
	def setOptions(self,optionName,value):
		""" Used to set the scenario options list in the loaded scenario. The scenario should load
			with the defaults but they can be altered to new values and then verrified. An active 
			session is required.
			Example URL: https://$MU_IP/api/v5/ddt/session/scenario/options/<optionName>/<value>
			
			Arguments:
			optionName - the full name of the option as it occures in the scenario. for example
				scenario_user_options.my_option_name
			value - the value to assigne to the option.
			
			Returns:
			MuConnection.MuResponseObject
		"""
		url = self.muConnection.TOP_LEVEL_URL + "/api/v5/ddt/session/scenario/options/" + optionName + "/" + value
		responseData = self.muConnection.post(url, None, None, True)
		self.session = responseData
		if self.VERBOSE:
			print responseData
		return responseData
		
	def setupTest(self):
		""" Used to set the test peramitors in the memory of the Mu Test Suite. This must be done
			bfore any test runs and after any host, channel, or option changes. An active session 
			is required.
			Example URL: https://$MU_IP/api/v5/ddt/session/setupTest
			
			Arguments:
			None
			
			Returns:
			MuConnection.MuResponseObject
		"""
		url = self.muConnection.TOP_LEVEL_URL + "/api/v5/ddt/session/setupTest"
		responseData = self.muConnection.post(url, None, None, True)
		self.session = responseData
		if self.VERBOSE:
			print responseData
		return responseData
		
	def tearDownTest(self):
		""" Used to remove the test peramitors in the memory of the Mu Test Suite. An active session 
			is required.
			Example URL: https://$MU_IP/api/v5/ddt/session/test/tearDownTest
			
			Arguments:
			None
			
			Returns:
			MuConnection.MuResponseObject
		"""
		url = self.muConnection.TOP_LEVEL_URL + "/api/v5/ddt/session/test/tearDownTest"
		responseData = self.muConnection.post(url, None, None, True)
		self.session = responseData
		if self.VERBOSE:
			print responseData
		return responseData

	def getTestSuiteResults(self):
		""" Used to retrieve the test results of the test suite from the Mu once it has completed a
			test run. An active session is required.
			Example URL: https://$MU_IP/api/v5/ddt/session/test/runSuite/results
			
			Arguments:
			None
			
			Returns:
			MuConnection.MuResponseObject
		"""
		url = self.muConnection.TOP_LEVEL_URL + "/api/v5/ddt/session/test/runSuite/results"
		responseData = self.muConnection.get(url,True)
		self.session = responseData
		if responseData.getRetrievedObjectType()==responseData.JSON and not responseData.hasError():
			jasonData = responseData.getJsonObject()
			tmpResponse = jasonData["testResults"]
			resultsListData = tmpResponse["results"]
			results = []
			for test in resultsListData:
				try:
					result = Result(test)
					results.append(result)
					if self.VERBOSE:
						print result
				except:
					print "-------------"
					print test
					print "-------------"
					print resultsListData
				
			responseData.setRetrievedObject(results)
			responseData.setRetrievedObjectType(responseData.RESULTS_LIST)
		if self.VERBOSE:
			print responseData
		return responseData
		
	
	def getTestSuiteStatus(self):
		""" Used to retrieve the test suite status of the loaded scenario from the Mu. An active 
			session is required.
			Example URL: https://$MU_IP/api/v5/ddt/session/test/runSuite/status
			
			Arguments:
			None
			
			Returns:
			MuConnection.MuResponseObject
		"""
		url = self.muConnection.TOP_LEVEL_URL + "/api/v5/ddt/session/test/runSuite/status"
		responseData = self.muConnection.get(url, True)
		self.session = responseData
		if self.VERBOSE:
			print responseData
		return responseData
	
	def runTestSuite(self, uuid):
		""" Used to run a test suite on the loaded scenario on the Mu. An active 
			session is required.
			Example URL: https://$MU_IP/api/v5/ddt/session/test/runSuite/<uuid>
			
			Arguments:
			uuid - the uuid value of the test suite that resides on the Mu
			
			Returns:
			MuConnection.MuResponseObject
		"""
		url = self.muConnection.TOP_LEVEL_URL + "/api/v5/ddt/session/test/runSuite/"+ uuid
		responseData = self.muConnection.post(url, None, None, True)
		self.session = responseData
		if self.VERBOSE:
			print responseData
		return responseData	
	
	def verifyScenario(self):
		""" Used to run a verification on the loaded scenario on the Mu using the options that have 
			been set. An active session is required.
			Example URL: https://$MU_IP/api/v5/ddt/session/test/run
			
			Arguments:
			None
			
			Returns:
			MuConnection.MuResponseObject
		"""
		url = self.muConnection.TOP_LEVEL_URL + "/api/v5/ddt/session/test/run"
		responseData = self.muConnection.post(url, None, None, True)
		self.session = responseData
		if self.VERBOSE:
			print responseData
		return responseData	
	
	def importCSV(self,csvFileName,newTemplateName):
		""" Used to import a csv file to the Mu in preperation of running a test suite. The csv file
			must be in the correct format. An active session is required.
			Example URL: https://$MU_IP/api/v5/ddt/session/test/import/csv
			
			Arguments:
			csvFileName - the name of the csv file including any path info needed to get there
				/myPath/myCsvFile.csv
			newTemplateName - this is the name to be placed in the mimeAtachment section.
			
			Returns:
			MuConnection.MuResponseObject
			
			TODO:
			newTemplateName option is not working
		"""
		url = self.muConnection.TOP_LEVEL_URL + "/api/v5/ddt/session/test/import/csv"
		responseData = self.muConnection.postMime(url,csvFileName,'file',newTemplateName,True)
		self.session = responseData
		if responseData.getRetrievedObjectType()==responseData.JSON and not responseData.hasError():
			jasonData = responseData.getJsonObject()
			tmpResponse = jasonData["response"]
			message = tmpResponse["message"]
			if message.find("Unable")>=0:
				responseData.setCompleted(False)
		if self.VERBOSE:
			print responseData
		return responseData	
		
	def importCSVData(self,csvData,newTemplateName):
		""" Used to import a csv data set to the Mu in preperation of running a test suite. The csv file
			must be in the correct format. An active session is required.
			Example URL: https://$MU_IP/api/v5/ddt/session/test/import/csv
			
			Arguments:
			csvFileName - the name of the csv file including any path info needed to get there
				/myPath/myCsvFile.csv
			newTemplateName - this is the name to be placed in the mimeAtachment section.
			
			Returns:
			MuConnection.MuResponseObject
			
			TODO:
			newTemplateName option is not working
		"""
		url = self.muConnection.TOP_LEVEL_URL + "/api/v5/ddt/session/test/import/csv"
		responseData = self.muConnection.postMime(url,csvData,'file',newTemplateName,True,False)
		self.session = responseData
		if responseData.getRetrievedObjectType()==responseData.JSON and not responseData.hasError():
			jasonData = responseData.getJsonObject()
			tmpResponse = jasonData["response"]
			message = tmpResponse["message"]
			if message.find("Unable")>=0:
				responseData.setCompleted(False)
		if self.VERBOSE:
			print responseData
		return responseData	
	
	def exportCSV(self, UUID, fileToSaveAs = None, fileMode = 't'):
		""" Used to export a csv file from the Mu.
			Example URL: https://$MU_IP/api/v5/ddt/session/test/export/csv/<uuid>
			
			Arguments:
			UUID - the uuid of the test suite on the Mu that will be exported
			fileToSaveAs - the path and name to save the csv data to. If not specified the data will
				only be in the MuResponseObject.
			fileMode - should always be t for text
			
			Returns:
			MuConnection.MuResponseObject
			
		"""
		# https://$MU_IP/api/v5/ddt/session/test/export/csv/${value}
		url = self.muConnection.TOP_LEVEL_URL + "/api/v5/ddt/session/test/export/csv/" + UUID
		responseData = self.muConnection.post(url, None, None, True)
		self.session = responseData
		if responseData.getRetrievedObjectType()==responseData.JSON and not responseData.hasError():
			jasonData = responseData.getJsonObject()
			tmpResponse = jasonData["response"]
			message = tmpResponse["message"]
			if message.find("Unable")>=0:
				print message
				responseData.setCompleted(False)
			elif fileToSaveAs!=None:
				# Open the local file for writing
				newFile = open(fileToSaveAs, "w" + fileMode)			
				newFile.write(message)
				newFile.close()
		if self.VERBOSE:
			print responseData
		
		return responseData			

class TestCase():
	""" This class will contain the data represented in a singe Mu Studio Verify test case.

		TODO: not implemented.
			
	"""

	name = ''

	def __init__(self,jsonData):
		print 'TODO'
	
	def __str__(self):
		print 'TODO'


class Result():
	""" This class represents a test result.

		Example JSON structure: {'status': 'Ok', 'testName': '1 Default Values(1)', 
			'testResult': 'PASSED', 'testOptions': {'options': [{'name': 'scenario_user_options.rootDoc', 
			'value': '/services/org'}, {'name': 'scenario_user_options.uri', 'value': '192.168.1.1'}, 
			{'name': 'io.timeout', 'value': 250}, {'name': 'scenario_user_options.responseCode', 
			'value': 200}]}}
		
		Variables:
		status - the status of the test execution: OK, ????
		testName - the name of the test case.
		testResult - the result of the test: PASSED, FAILED
		testOptions - the options of the test when it ran. unlike the options list in the testSuite this 
			should contain all options and there values.
	
		Methods:
		__init__ - create result based on json data from the mu
		__str__ - print it nice and prety
		loadOptions - load a list of class MuTestApi.option
		load - load test result from passed objects
		set/getStatus - set or get methods for the status
		set/getTestName - set or get methods for the test name
		set/getTestResult - set or get methods for the test result
		set/getTestOptions - set or get methods for the test options
	"""
	# 
	status = ''
	testName = ''
	testResult = ''
	testOptions = []
	
	def __init__(self,jsonData):
		#print "---- init result -----" 
		if jsonData == None:
			return
		self.status = jsonData['status']
		self.testResult = jsonData['testResult']
		if self.testResult == "END":
			return
		self.testName = jsonData['testName']
		tmpdata = jsonData['testOptions']
		#print tmpdata
		tmpdata = tmpdata['options']
		#print tmpdata
		self.loadOptions(tmpdata)

		
	def __str__(self):
		#value = "{ status : " + self.status + " , testName : " + self.testName + ", testResult : " + self.testResult + ", testOptions : ["
		#for option in self.testOptions:
		#	value = value
		return "{ status : " + self.status + " , testName : " + self.testName + ", testResult : " + self.testResult + ", testOptions : [" + ",".join(str(v) for v in self.testOptions) + "]}"
	
	def getAsCsv(self,headers=False):
		#print "get csv from Result()"
		returnValue = ""
		if headers:
			returnValue = '"testName","status","testResult"'
			count = 0
			for option in self.testOptions:
				returnValue = returnValue + ',"option.'+str(count)+'"'
				count = count + 1
			returnValue = returnValue + '\r'
		else:
			returnValue = returnValue + '"'+self.testName+'","'+self.status+'","'+self.testResult+'"'
			for option in self.testOptions:
				returnValue = returnValue + ',"'+str(option)+'"'
			returnValue = returnValue + '\r'
		#print "get csv from Result() return"
		return returnValue
	
	def loadOptions(self, optionsList):
		self.testOptions = []
		for itum in optionsList:
			#print itum
			if itum == 'name':
				option = Option(optionsList)
				self.testOptions.append(option)
				return
			else:
				option = Option(itum)
			self.testOptions.append(option)
	
	def load(self, status='',testName='',testResult='',testOptions=[]):
		self.status = status
		self.testName = testName
		self.testResult = testResult
		self.testOptions = testOptions
				
		
	def getStatus(self):
		return self.status
		
	def getTestName(self):
		return self.testName
		
	def getTestResult(self):
		return self.testResult
		
	def getTestOptions(self):
		return self.testOptions
	
	
class Option():
	""" This class represents a test option.

		Example JSON structure: {'name': 'scenario_user_options.rootDoc', 'value': '/services/org'}
		
		Variables:
		name - the full option name
		value - the option value
	
		Methods:
		__init__ - create result based on json data from the mu
		__str__ - print it nice and prety
		set/getName - set or get methods for the name
		set/getValue - set or get methods for the test value
	"""
	# {'name': 'scenario_user_options.rootDoc', 'value': '/services/org'}
	name = ''
	value = ''
	
	def __init__(self,jsonData):
		#print "---- init option -----" 
		self.name = jsonData['name']
		self.value = jsonData['value']
		
	def __str__(self):
		return "{name: "+self.name+", value: "+str(self.value)+"}"
	
	def getName(self):
		return self.name
		
	def getValue(self):
		return self.value
		
	def setName(self, name):
		self.name = name
	
	def setValue(self,value):
		self.value = value